home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 7
/
Aminet 7 - August 1995.iso
/
Aminet
/
comm
/
tcp
/
AmigaTCP.lha
/
AmigaTCP
/
src
/
tcpuser.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-06-24
|
6KB
|
281 lines
/* User calls to TCP */
#include "machdep.h"
#include "timer.h"
#include "mbuf.h"
#include "netuser.h"
#include "internet.h"
#include "ip.h"
#include "tcp.h"
int16 tcp_window = DEF_WND;
struct tcb *
open_tcp(lsocket,fsocket,active,window,r_upcall,t_upcall,s_upcall,tos,user)
struct socket *lsocket; /* Local socket */
struct socket *fsocket; /* Remote socket */
int active; /* Active/passive */
int16 window; /* Receive window (and send buffer) sizes */
void (*r_upcall)(); /* Function to call when data arrives */
void (*t_upcall)(); /* Function to call when ok to send more data */
void (*s_upcall)(); /* Function to call when connection state changes */
char tos;
int *user; /* User linkage area */
{
struct connection conn;
register struct tcb *tcb;
void send_syn();
if(lsocket == NULLSOCK){
net_error = INVALID;
return NULLTCB;
}
conn.local.address = lsocket->address;
conn.local.port = lsocket->port;
if(fsocket != NULLSOCK){
conn.remote.address = fsocket->address;
conn.remote.port = fsocket->port;
} else {
conn.remote.address = 0;
conn.remote.port = 0;
}
if((tcb = lookup_tcb(&conn)) == NULLTCB){
if((tcb = create_tcb(&conn)) == NULLTCB){
net_error = NO_SPACE;
return NULLTCB;
}
} else if(tcb->state != LISTEN){
net_error = CON_EXISTS;
return NULLTCB;
}
tcb->user = user;
if(window != 0)
tcb->window = tcb->rcv.wnd = window;
else
tcb->window = tcb->rcv.wnd = tcp_window;
tcb->r_upcall = r_upcall;
tcb->t_upcall = t_upcall;
tcb->s_upcall = s_upcall;
tcb->tos = tos;
if(!active){
setstate(tcb,LISTEN);
return tcb;
}
/* Send SYN, go into SYN_SENT state */
send_syn(tcb);
setstate(tcb,SYN_SENT);
tcp_output(tcb);
tcp_stat.conout++;
return tcb;
}
/* User send routine */
int
send_tcp(tcb,bp)
register struct tcb *tcb;
struct mbuf *bp;
{
int16 cnt;
if(tcb == NULLTCB || bp == NULLBUF){
free_p(bp);
net_error = INVALID;
return -1;
}
cnt = len_mbuf(bp);
#ifdef TIGHT
/* If this would overfill our send queue, reject it entirely */
if(tcb->sndcnt + cnt > tcb->window){
free_p(bp);
net_error = WOULDBLK;
return -1;
}
#endif
switch(tcb->state){
case CLOSED:
free_p(bp);
net_error = NO_CONN;
return -1;
case LISTEN: /* Change state from passive to active */
send_syn(tcb);
setstate(tcb,SYN_SENT); /* Note fall-thru */
case SYN_SENT:
case SYN_RECEIVED:
case ESTABLISHED:
case CLOSE_WAIT:
append(&tcb->sndq,bp);
tcb->sndcnt += cnt;
tcp_output(tcb);
break;
case FINWAIT1:
case FINWAIT2:
case CLOSING:
case LAST_ACK:
case TIME_WAIT:
free_p(bp);
net_error = CON_CLOS;
return -1;
}
return cnt;
}
/* User receive routine */
int
recv_tcp(tcb,bp,cnt)
register struct tcb *tcb;
struct mbuf **bp;
int16 cnt;
{
if(tcb == NULLTCB || bp == (struct mbuf **)NULL){
net_error = INVALID;
return -1;
}
/* cnt == 0 means "I want it all" */
if(cnt == 0)
cnt = tcb->rcvcnt;
/* If there's something on the queue, just return it regardless
* of the state we're in.
*/
if(tcb->rcvcnt != 0){
/* See if the user can take all of it */
if(tcb->rcvcnt <= cnt){
cnt = tcb->rcvcnt;
*bp = tcb->rcvq;
tcb->rcvq = NULLBUF;
} else {
if((*bp = alloc_mbuf(cnt)) == NULLBUF){
net_error = NO_SPACE;
return -1;
}
pullup(&tcb->rcvq,(*bp)->data,cnt);
(*bp)->cnt = cnt;
}
tcb->rcvcnt -= cnt;
tcb->rcv.wnd += cnt;
/* Do a window update if it was closed */
if(cnt == tcb->rcv.wnd){
tcb->force = 1;
tcp_output(tcb);
}
return cnt;
} else {
/* If there's nothing on the queue, our action depends on what state
* we're in (i.e., whether or not we're expecting any more data).
* If no more data is expected, then simply return 0; this is
* interpreted as "end of file".
*/
switch(tcb->state){
case LISTEN:
case SYN_SENT:
case SYN_RECEIVED:
case ESTABLISHED:
case FINWAIT1:
case FINWAIT2:
*bp = NULLBUF;
net_error = WOULDBLK;
return -1;
case CLOSED:
case CLOSE_WAIT:
case CLOSING:
case LAST_ACK:
case TIME_WAIT:
*bp = NULLBUF;
return 0;
}
}
return 0; /* Not reached, but lint doesn't know that */
}
/* This really means "I have no more data to send". It only closes the
* connection in one direction, and we can continue to receive data
* indefinitely.
*/
int
close_tcp(tcb)
register struct tcb *tcb;
{
if(tcb == NULLTCB){
net_error = INVALID;
return -1;
}
switch(tcb->state){
case LISTEN:
case SYN_SENT:
close_self(tcb,NORMAL);
return 0;
case SYN_RECEIVED:
case ESTABLISHED:
tcb->sndcnt++;
tcb->snd.nxt++;
setstate(tcb,FINWAIT1);
tcp_output(tcb);
return 0;
case CLOSE_WAIT:
tcb->sndcnt++;
tcb->snd.nxt++;
setstate(tcb,LAST_ACK);
tcp_output(tcb);
return 0;
case FINWAIT1:
case FINWAIT2:
case CLOSING:
case LAST_ACK:
case TIME_WAIT:
net_error = CON_CLOS;
return -1;
}
return -1; /* "Can't happen" */
}
/* Delete TCB, free resources. The user is not notified, even if the TCB is
* not in the CLOSED state. This function should normally be called by the
* user only in response to a state change upcall to CLOSED state.
*/
int
del_tcp(tcb)
register struct tcb *tcb;
{
void unlink_tcb();
struct reseq *rp,*rp1;
if(tcb == NULLTCB){
net_error = INVALID;
return -1;
}
unlink_tcb(tcb);
stop_timer(&tcb->timer);
for(rp = tcb->reseq;rp != NULLRESEQ;rp = rp1){
rp1 = rp->next;
free_p(rp->bp);
free((char *)rp);
}
tcb->reseq = NULLRESEQ;
free_p(tcb->rcvq);
free_p(tcb->sndq);
free((char *)tcb);
return 0;
}
/* Do printf on a tcp connection */
/*VARARGS*/
tprintf(tcb,message,arg1,arg2)
struct tcb *tcb;
char *message,*arg1,*arg2;
{
struct mbuf *bp;
int16 len;
char *cp,*index();
if(tcb == NULLTCB)
return 0;
len = strlen(message) + 10; /* fudge factor */
if((cp = index(message,'%')) != NULLCHAR){
/* What a gross hack! */
len += strlen(arg1);
if((cp = index(cp+1,'%')) != NULLCHAR){
/* I don't believe I'm writing this */
len += strlen(arg2);
}
}
bp = alloc_mbuf(len);
len = sprintf(bp->data,message,arg1,arg2);
bp->cnt = strlen(bp->data);
send_tcp(tcb,bp);
return len;
}